package net.slteam.base;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import io.swagger.v3.oas.annotations.media.Schema;
import org.apache.commons.lang3.exception.ExceptionUtils;

import java.util.HashMap;
import java.util.Map;

@Schema(description = "通用返回格式")
@JsonInclude(JsonInclude.Include.NON_NULL)
public class HttpResult<T> extends Object {
    @Schema(description = "状态, 1 成功 0 失败 2 等待 3 异常")
    int status;

    @Schema(description = "时间戳", example = "1541663875966")
//    @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
    long timestamp;

    @Schema(description = "错误码")
    Integer errorCode = 0;
    @Schema(description = "提示信息")
    @JSONField(name = "msg")
    @JsonAlias("msg")
    @JsonProperty("msg")
    String message;
    /**
     * 为了兼容Oauth2
     */
    @Schema(description = "错误信息")
    String error;
    @Schema(description = "返回对象")
    @JSONField(name = "data")
    @JsonAlias("data")
    @JsonProperty("data")
    @JsonInclude
    T result;

    public HttpResult() {
    }


    public HttpResult(int status,
                      long timestamp,
                      Integer errorCode,
                      String message,
                      String error,
                      T result) {
        this.status = status;
        this.timestamp = timestamp;
        this.errorCode = errorCode;
        this.message = message;
        this.error = error;
        this.result = result;
    }

    public static <T> HttpResultBuilder<T> builder() {
        return new HttpResultBuilder();
    }

    public static <T> HttpResult success(T o) {
        if (o instanceof String) {
            return HttpResult.builder().status(1).timestamp(System.currentTimeMillis()).message((String) o).result(o).build();
        } else {
            return HttpResult.builder().status(1).timestamp(System.currentTimeMillis()).result(o).build();
        }
    }

    @SuppressWarnings(value = "rawtypes")
    public static HttpResult error(IError e) {
        return HttpResult.builder().status(0).timestamp(System.currentTimeMillis()).errorCode(e.getCode()).error(e.getMessage()).build();
    }

    public static HttpResult error(SLRunTimeError e) {
        return HttpResult.builder().status(0).timestamp(System.currentTimeMillis()).errorCode(e.getCode()).error(e.getMessage()).build();
    }

    public static HttpResult error(SLError e) {
        return HttpResult.builder().status(0).timestamp(System.currentTimeMillis()).errorCode(e.getCode()).error(e.getMessage()).build();
    }

    public static HttpResult error(String e) {
        return HttpResult.builder().status(0).timestamp(System.currentTimeMillis()).error(e).build();
    }

    public static HttpResult exception(Throwable e) {
        return HttpResult.builder().status(3).timestamp(System.currentTimeMillis()).result(ExceptionUtils.getStackTrace(e)).build();
    }

    public static <T> HttpResult wait(T o) {
        if (o instanceof String) {
            return HttpResult.builder().status(2).timestamp(System.currentTimeMillis()).message((String) o).build();
        } else {
            return HttpResult.builder().status(2).timestamp(System.currentTimeMillis()).result(o).build();
        }
    }

    public int getStatus() {
        return this.status;
    }

    public void setStatus(final int status) {
        this.status = status;
    }

    public long getTimestamp() {
        return this.timestamp;
    }

    public void setTimestamp(final long timestamp) {
        this.timestamp = timestamp;
    }

    public Integer getErrorCode() {
        return this.errorCode;
    }

    public void setErrorCode(final Integer errorCode) {
        this.errorCode = errorCode;
    }

    public String getMessage() {
        return this.message;
    }

    public HttpResult setMessage(String message) {
        this.message = message;
        return this;
    }

    public String getError() {
        return this.error;
    }

    public void setError(final String error) {
        this.error = error;
    }

    public T getResult() {
        return this.result;
    }

    @JsonAlias({"data"})
    @JsonProperty("data")
    public void setResult(final T result) {
        this.result = result;
    }

    @SuppressWarnings("unchecked")
    public Object toJson() {
        Map m = new HashMap<String, Object>();
        m.put("status", status);
        m.put("timestamp", timestamp);
        if (result != null) {
            m.put("result", result);
        }
        if (errorCode != null) {
            m.put("errorCode", errorCode);
        }
        if (message != null) {
            m.put("message", message);
        }
        if (message != null) {
            m.put("error", error);
        }

        return m;
    }

    @Override
    public String toString() {
        return JSON.toJSONString(this);
    }

    public static class HttpResultBuilder<T> {
        private int status;
        private long timestamp;
        private Integer errorCode;
        private String message;
        private String error;
        private T result;

        HttpResultBuilder() {
        }

        public HttpResultBuilder<T> status(final int status) {
            this.status = status;
            return this;
        }

        public HttpResultBuilder<T> timestamp(final long timestamp) {
            this.timestamp = timestamp;
            return this;
        }

        public HttpResultBuilder<T> errorCode(final Integer errorCode) {
            this.errorCode = errorCode;
            return this;
        }

        @JsonAlias({"msg"})
        @JsonProperty("msg")
        public HttpResultBuilder<T> message(final String message) {
            this.message = message;
            return this;
        }

        public HttpResultBuilder<T> error(final String error) {
            this.error = error;
            return this;
        }

        @JsonAlias({"data"})
        @JsonProperty("data")
        public HttpResultBuilder<T> result(final T result) {
            this.result = result;
            return this;
        }

        public HttpResult<T> build() {
            return new HttpResult(this.status, this.timestamp, this.errorCode, this.message, this.error, this.result);
        }

        @Override
        public String toString() {
            return "HttpResult.HttpResultBuilder(status=" + this.status + ", timestamp=" + this.timestamp + ", errorCode=" + this.errorCode + ", message=" + this.message + ", error=" + this.error + ", result=" + this.result + ")";
        }
    }
}
